//
// Created by gxx on 2023/11/12.
//
#include "util/logger.h"
#include <stdarg.h>
#include <thread>
#include <sstream>
#include <sys/stat.h>

namespace TinyDB {

Logger::Logger() {}

Logger::~Logger() {}

t_string Logger::level_name_[LOG_LEVEL_COUNT] = {
"[TRACE]",
"[DEBUG]",
"[INFO]",
"[WARN]",
"[ERROR]",
"[FATAL]"
};

Logger* Logger::instance_ = nullptr;
std::mutex Logger::mtx_;
std::mutex Logger::file_mtx_;
LOG_LEVEL Logger::log_level_ = LOG_LEVEL_TRACE;
t_uint8 Logger::log_target_ = 0b00000001;
FILE* Logger::fp_ = nullptr;
t_uint32 Logger::year_ = 0;
t_uint32 Logger::mouth_ = 0;
t_uint32 Logger::day_ = 0;

Logger* Logger::getInstance() {
	if (instance_ == nullptr) {
		{
      std::lock_guard<std::mutex> guard(mtx_);
			if (instance_ == nullptr) {
				instance_ = new Logger();
			}
		}
	}
	return instance_;
}

t_void Logger::SetLogLevel(LOG_LEVEL level) {
	log_level_ = level;
}

t_void Logger::SetLogTarget(LOG_TARGET target) {
	switch (target) {
		case LOG_TARGET_CONSOLE:
			log_target_ = SET_BIT_1(log_target_, LOG_TARGET_CONSOLE);
			break;
		case LOG_TARGET_FILE:
			log_target_ = SET_BIT_1(log_target_, LOG_TARGET_FILE);
			break;
		case LOG_TARGET_BOTH:
			log_target_ = SET_BIT_1(log_target_, LOG_TARGET_CONSOLE);
			log_target_ = SET_BIT_1(log_target_, LOG_TARGET_FILE);
			break;
		default:
			::fprintf(stdout, "%s\n", "LOG_TARGET input error.");
	}

	if (GET_BIT(log_target_, LOG_TARGET_FILE)) {
		if (fp_ == nullptr) {
      std::lock_guard<std::mutex> grard(file_mtx_);
			if (fp_ == nullptr) {
				::mkdir("./log/", 0777);
				fp_ = GenFile();
			}
		}
	}

}


t_void Logger::WriteLog(TinyDB::LOG_LEVEL log_level, const t_char* file_name, t_uint32 line, const t_char* format, ...) {
	// 日志的基本格式为: 时间戳 | 日志级别 | 线程ID |所在文件 ｜ 所在行 ｜ 用户的日志消息
	if (log_level < log_level_) {
		return;
	}

	time_t ticks = time(nullptr);
	struct tm* ptm = localtime(&ticks);
	char timestamps[32] = {0};
	strftime(timestamps, sizeof(timestamps), "%Y-%m-%d %H:%M:%S", ptm);

	va_list v_list;
	va_start(v_list, format);
	t_char msg[1024] = {0};
	vsnprintf(msg, 1024, format, v_list);
	va_end(v_list);

	std::stringstream ts;
	ts << std::this_thread::get_id();
	t_string thread_id_str = ts.str();

	const t_char* fmt = "%s %s thread_id:%s %s %s %s\n";
	t_string line_str = std::to_string(line);
	t_char log_str[4096] = {0};
	t_uint32 wn = snprintf(log_str, 4096, fmt, timestamps, level_name_[log_level].c_str(),
					 thread_id_str.c_str(), file_name, line_str.c_str(), msg);

	if (GET_BIT(log_target_, LOG_TARGET_CONSOLE)) {
		PrintToConsole(log_str, wn);
	}
	if (GET_BIT(log_target_, LOG_TARGET_FILE)) {
		PrintToFile(log_str, wn);
	}

}

t_void Logger::PrintToConsole(const t_char *msg, t_uint32 n) {
	if (n <= 0) {
		return;
	}
	::fprintf(stdout, "%s", msg);
}

t_void Logger::PrintToFile(const t_char *msg, t_uint32 n) {
	if (n <= 0 || fp_ == nullptr) {
		return;
	}

	{
    std::lock_guard<std::mutex> guard(file_mtx_);
		time_t ticks = time(nullptr);
		struct tm* ptm = localtime(&ticks);
		t_uint32 cur_year = ptm->tm_year;
		t_uint32 cur_mouth = ptm->tm_mon;
		t_uint32 cur_day = ptm->tm_mday;
		if (cur_year != year_ || cur_mouth != mouth_ || cur_day != day_) {
			::fclose(fp_);
			fp_ = GenFile();
		}
		::fprintf(fp_, "%s", msg);
		::fflush(fp_);
	}

}

FILE *Logger::GenFile() {
	time_t ticks = time(nullptr);
	struct tm* ptm = localtime(&ticks);
	year_ = ptm->tm_year;
	mouth_ = ptm->tm_mon;
	day_ = ptm->tm_mday;

	char file_name[50] = {0};
	strftime(file_name, sizeof(file_name), "./log/TDB-log-%Y-%m-%d-%H-%M-%S.log", ptm);
	FILE* fp = nullptr;
	fp = fopen(file_name, "a+");

	return fp;
}

}




